<!DOCTYPE html>


  <html class="dark page-post">


<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>前端性能之Performance以及动画帧率(FPS) | Poetry&#39;s Blog</title>

  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
    <meta name="keywords" content="FPS,Performance,">
  

  <meta name="description" content="Performance是一个做前端性能监控离不开的API，最好在页面完全加载完成之后再使用，因为很多值必须在页面完全加载之后才能得到。最简单的办法是在window.onload事件中读取各种数据。  一、回顾页面加载过程 要学习这套API的使用，先简单介绍下前端的基础知识  1.1 页面加载 一个页面的请求到响应再到显示出来，需要经过下面一些重要过程，当我们在浏览器输入一个URL或者说点击一个U">
<meta name="keywords" content="FPS,Performance">
<meta property="og:type" content="article">
<meta property="og:title" content="前端性能之Performance以及动画帧率(FPS)">
<meta property="og:url" content="http://blog.poetries.top/2018/11/20/performance-fps/index.html">
<meta property="og:site_name" content="Poetry&#39;s Blog">
<meta property="og:description" content="Performance是一个做前端性能监控离不开的API，最好在页面完全加载完成之后再使用，因为很多值必须在页面完全加载之后才能得到。最简单的办法是在window.onload事件中读取各种数据。  一、回顾页面加载过程 要学习这套API的使用，先简单介绍下前端的基础知识  1.1 页面加载 一个页面的请求到响应再到显示出来，需要经过下面一些重要过程，当我们在浏览器输入一个URL或者说点击一个U">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://img-blog.csdn.net/20170316140524418">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33872196-d6c35844-df50-11e7-8bcc-1fdcac66ce64.png">
<meta property="og:image" content="https://img-blog.csdn.net/20170316155201356">
<meta property="og:image" content="https://img-blog.csdn.net/20170316155226893">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33823997-e25d42dc-de97-11e7-92e3-8521ede019df.png">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33830985-4ab23108-deb1-11e7-89ed-6b9f60dcb2fd.jpg">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33871575-0799a66a-df4e-11e7-85fd-9b40d43161da.png">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33872196-d6c35844-df50-11e7-8bcc-1fdcac66ce64.png">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33873788-4ad802e2-df57-11e7-9174-13913bcaa9b5.png">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33877304-eae6fddc-df62-11e7-8d5f-8674dba3d1f9.png">
<meta property="og:image" content="https://user-images.githubusercontent.com/8554143/33877626-ed17f3c6-df63-11e7-8bae-40b9512c2645.gif">
<meta property="og:updated_time" content="2020-08-15T04:25:31.930Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="前端性能之Performance以及动画帧率(FPS)">
<meta name="twitter:description" content="Performance是一个做前端性能监控离不开的API，最好在页面完全加载完成之后再使用，因为很多值必须在页面完全加载之后才能得到。最简单的办法是在window.onload事件中读取各种数据。  一、回顾页面加载过程 要学习这套API的使用，先简单介绍下前端的基础知识  1.1 页面加载 一个页面的请求到响应再到显示出来，需要经过下面一些重要过程，当我们在浏览器输入一个URL或者说点击一个U">
<meta name="twitter:image" content="https://img-blog.csdn.net/20170316140524418">

  

  
    <link rel="icon" href="/favicon.ico">
  

  <link href="/css/styles.css?v=c114cbeddx" rel="stylesheet">
<link href="/css/other.css?v=c114cbeddx" rel="stylesheet">


  
    <link rel="stylesheet" href="/css/personal-style.css">
  

  

  
  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?40b1f89aa80f2527b3db779c6898c879";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>


  
  <script type="text/javascript">
	(function(){
	    var bp = document.createElement('script');
	    var curProtocol = window.location.protocol.split(':')[0];
	    if (curProtocol === 'https') {
	        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
	    }
	    else {
	        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
	    }
	    var s = document.getElementsByTagName("script")[0];
	    s.parentNode.insertBefore(bp, s);
	})();
  </script>



  
    <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css">
  

  <!-- 聊天系统 -->
  
    
   <link type="text/css" rel="stylesheet" href="/renxi/default.css">
   <style>
      #modal {
        position: static !important;
      }
      .filter {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        background: #fe5757;
        animation: colorChange 30s ease-in-out infinite;
        animation-fill-mode: both;
        mix-blend-mode: overlay;
      }
  
      @keyframes colorChange {
        0%, 100% {
            opacity: 0;
        }
        50% {
            opacity: .9;
        }
      }
   </style>
</head>
</html>
<body>
  
  
    <span id="toolbox-mobile" class="toolbox-mobile">导航</span>
  

  <div class="post-header CENTER">
   
  <div class="toolbox">
    <a class="toolbox-entry" href="/">
      <span class="toolbox-entry-text">导航</span>
      <i class="icon-angle-down"></i>
      <i class="icon-home"></i>
    </a>
    <ul class="list-toolbox">
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/archives/"
            rel="noopener noreferrer"
            target="_self"
            >
            博客
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/categories/"
            rel="noopener noreferrer"
            target="_self"
            >
            分类
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/tags/"
            rel="noopener noreferrer"
            target="_self"
            >
            标签
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/search/"
            rel="noopener noreferrer"
            target="_self"
            >
            搜索
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/link/"
            rel="noopener noreferrer"
            target="_self"
            >
            友链
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/about/"
            rel="noopener noreferrer"
            target="_self"
            >
            关于
          </a>
        </li>
      
    </ul>
  </div>


</div>


  <div id="toc" class="toc-article">
    <strong class="toc-title">文章目录<i class="iconfont toc-title" style="display:inline-block;color:#87998d;width:20px;height:20px;">&#xf004b;</i></strong>
    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#一、回顾页面加载过程"><span class="toc-text">一、回顾页面加载过程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-1-页面加载"><span class="toc-text">1.1 页面加载</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1-2-重定向分析"><span class="toc-text">1.2 重定向分析</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#二、performance"><span class="toc-text">二、performance</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-1-performance-timing"><span class="toc-text">2.1 performance.timing</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-performance-getEntries"><span class="toc-text">2.2 performance.getEntries()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-performance-memory"><span class="toc-text">2.3 performance.memory</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#三、动画帧率FPS"><span class="toc-text">三、动画帧率FPS</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-流畅动画的标准"><span class="toc-text">3.1 流畅动画的标准</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-获取我们页面动画当前的-FPS-值"><span class="toc-text">3.2 获取我们页面动画当前的 FPS 值</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#3-2-1-方法一-借助-Chrome-开发者工具"><span class="toc-text">3.2.1 方法一 借助 Chrome 开发者工具</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-2-2-方法二-借助-Frame-Timing-API"><span class="toc-text">3.2.2 方法二 借助 Frame Timing API</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-2-3-方法三-借助-requestAnimationFrame-API"><span class="toc-text">3.2.3 方法三 借助 requestAnimationFrame API</span></a></li></ol></li></ol></li></ol>
  </div>
  




<div class="content content-post CENTER">
   <!-- canvas 彩带 -->
<canvas id="evanyou" width="1302" height="678" style="position: fixed;width: 100%;height: 100%;top: 0;left:0;z-index:-1;"></canvas>

<article id="post-performance-fps" class="article article-type-post" itemprop="blogPost">
  <header class="article-header" style="position:relative;">
    <h1 class="post-title">前端性能之Performance以及动画帧率(FPS)</h1>

    <div class="article-meta">
      <span>
        <i class="icon-calendar"></i>
        <span>2018.11.21</span>
      </span>

      
        <span class="article-author">
          <i class="icon-user"></i>
          <span>Poetry</span>
        </span>
      

      
  <span class="article-category">
    <i class="icon-list"></i>
    <a class="article-category-link" href="/categories/Front-End/">Front-End</a>
  </span>



      

      
      <i class="fa fa-eye"></i> 
        <span id="busuanzi_container_page_pv">
           &nbsp热度 <span id="busuanzi_value_page_pv">
           <i class="fa fa-spinner fa-spin"></i></span>℃
        </span>
      
      
       
          <span class="post-count">
            <i class="fa fa-file-word-o"></i>&nbsp
            <span>字数统计 3.4k字</span>
          </span>

          <span class="post-count">
            <i class="fa fa-columns"></i>&nbsp
            <span>阅读时长 12分</span>
          </span>
      
      
    </div>

    <i class="iconfont" id="toc-eye" style="display:inline-block;color:#b36619;position:absolute;top:0;right:0;cursor:pointer;
    font-size: 24px;">&#xe61c;</i>

  </header>

  <div class="article-content">
    
      <div id="container">
        <blockquote>
<p><code>Performance</code>是一个做前端性能监控离不开的<code>API</code>，最好在页面完全加载完成之后再使用，因为很多值必须在页面完全加载之后才能得到。最简单的办法是在<code>window.onload</code>事件中读取各种数据。</p>
</blockquote>
<h2 id="一、回顾页面加载过程"><a href="#一、回顾页面加载过程" class="headerlink" title="一、回顾页面加载过程"></a>一、回顾页面加载过程</h2><blockquote>
<p>要学习这套<code>API</code>的使用，先简单介绍下前端的基础知识</p>
</blockquote>
<h3 id="1-1-页面加载"><a href="#1-1-页面加载" class="headerlink" title="1.1 页面加载"></a>1.1 页面加载</h3><blockquote>
<p>一个页面的请求到响应再到显示出来，需要经过下面一些重要过程，当我们在浏览器输入一个<code>URL</code>或者说点击一个<code>URL</code>开始，会出现如下流程</p>
</blockquote>
<ul>
<li>页面准备</li>
<li>重定向：在<code>header</code>定义了重定向才会有这个过程，如果没有重定向，不会产生这个过程。</li>
<li><code>app cache</code>：会先检查这个域名是否有缓存，如果有缓存就不需要DNS解析域名。这里的<code>app</code>是值应用程序<code>application</code>，不指手机<code>app</code>。</li>
<li><code>DNS</code>解析：把域名解析成<code>IP</code>，如果直接用<code>ip</code>地址访问，不产生这个过程。</li>
<li><code>TCP</code>连接：<code>http</code>协议是经过<code>TCP</code>来传输的，所以产生一个<code>http</code>请求就会有<code>TCP connect</code>，但是依赖于长连接，不会产生这个过程。</li>
<li><code>request header</code>：请求头信息。</li>
<li><code>request body</code>：请求体信息，比如<code>get</code>请求是没有请求体信息的，所以没有这个过程，这就是为什么把头跟体分开写的原因。</li>
<li><code>response header</code>：响应头信息。</li>
<li><code>response body</code>：响应体信息。</li>
<li>解析<code>HTML</code>结构</li>
<li>加载外部脚本和样式表文件：正常来说<code>JS</code>、<code>css</code>都是外部加载的，当然有不正常的人啊，比如我。</li>
<li>解析并执行脚本代码</li>
<li>构建与解析<code>HTML DOM</code>树：这个过程可以去了解下<code>DOM</code>树是怎样的就明白啦。</li>
<li>加载外部图片</li>
<li>页面加载完成，显示出来啦</li>
</ul>
<h3 id="1-2-重定向分析"><a href="#1-2-重定向分析" class="headerlink" title="1.2 重定向分析"></a>1.2 重定向分析</h3><ul>
<li><code>app cach</code></li>
<li><code>DNS</code>解析</li>
<li><code>TCP</code>连接</li>
<li><code>request header</code></li>
<li>重定向</li>
<li><code>app cach</code></li>
<li><code>DNS</code>解析</li>
<li><code>TCP</code>连接</li>
<li><code>request header</code></li>
</ul>
<h2 id="二、performance"><a href="#二、performance" class="headerlink" title="二、performance"></a>二、performance</h2><h3 id="2-1-performance-timing"><a href="#2-1-performance-timing" class="headerlink" title="2.1 performance.timing"></a>2.1 performance.timing</h3><blockquote>
<p>这个API能帮我们得到整个页面请求的时间，如下图，在<code>Chrome</code>的<code>Console</code>是可以直接运行的</p>
</blockquote>
<p><img src="https://img-blog.csdn.net/20170316140524418" alt></p>
<p>先解释下这些时间都是代表什么</p>
<p><strong>timing 对象里边的数据比较多，梳理如下几个关键性的节点</strong></p>
<ul>
<li><code>fetchStart</code>：发起获取当前文档的时间点，我的理解是浏览器收到发起页面请求的时间点；</li>
<li><code>domainLookupStart</code>：返回浏览器开始<code>DNS</code>查询的时间，如果此请求没有<code>DNS</code>查询过程，如长连接、资源<code>cache</code>、甚至是本地资源等，那么就返回<code>fetchStart</code>的值；</li>
<li><code>domainLookupEnd</code>：返回浏览器结束<code>DNS</code>查询的时间，如果没有<code>DNS</code>查询过程，同上；</li>
<li><code>connectStart</code>：浏览器向服务器请求文档，开始建立连接的时间，如果此连接是一个长连接，或者无需与服务器连接（命中缓存），则返回<code>domainLookupEnd</code>的值；</li>
<li><code>connectEnd</code>：浏览器向服务器请求文档，建立连接成功的时间；</li>
<li><code>requestStart</code>：开始请求文档的时间（注意没有<code>requestEnd</code>）;</li>
<li><code>responseStart</code>：浏览器开始接收第一个字节数据的时间，数据可能来自于服务器、缓存、或本地资源；</li>
<li><code>unloadEventStart</code>：卸载上一个文档开始的时间；</li>
<li><code>unloadEventEnd</code>：卸载上一个文档结束的时间；</li>
<li><code>domLoading</code>：浏览器把<code>document.readyState</code>设置为<code>“loading”</code>的时间点，开始构建<code>dom</code>树的时间点；</li>
<li><code>responseEnd</code>：浏览器接收最后一个字节数据的时间，或连接被关闭的时间；</li>
<li><code>domInteractive</code>：浏览器把<code>document.readyState设</code>置为<code>“interactive”</code>的时间点，<code>DOM</code>树创建结束；</li>
<li><code>domContentLoadedEventStart</code>：文档发生<code>DOMContentLoaded</code>事件的时间；</li>
<li><code>domContentLoadedEventEnd</code>：文档的<code>DOMContentLoaded</code>事件结束的时间；</li>
<li><code>domComplete</code>：浏览器把<code>document.readyState</code>设置为<code>“complete”</code>的时间点；</li>
<li><code>loadEventStart</code>：文档触发<code>load</code>事件的时间；</li>
<li><code>loadEventEnd</code>：文档出发<code>load</code>事件结束后的时间</li>
</ul>
<blockquote>
<p>再来一张图，表示各阶段的开始与结束对应的时间</p>
</blockquote>
<p><img src="https://user-images.githubusercontent.com/8554143/33872196-d6c35844-df50-11e7-8bcc-1fdcac66ce64.png" alt></p>
<blockquote>
<p>从以上的分析，我们就可以得到一些时间的计算</p>
</blockquote>
<ul>
<li>准备新页面耗时：<code>fetchStart - navigationStart</code></li>
<li>重定向时间：<code>redirectEnd - redirectStart</code></li>
<li><code>App Cache</code>时间：<code>domainLookupStart - fetchStart</code></li>
<li><code>DNS</code>解析时间：<code>domainLookupEnd -domainLookupStart</code></li>
<li><code>TCP</code>连接时间：<code>connectEnd - connectStart</code></li>
<li><code>request</code>时间：<code>responseEnd - requestStart</code>这个计算是代表请求响应加起来的时间</li>
<li>请求完毕到<code>DOM</code>树加载：<code>domInteractive -responseEnd</code></li>
<li>构建与解析<code>DOM</code>树，加载资源时间：<code>domCompleter -domInteractive</code></li>
<li><code>load</code>时间：<code>loadEventEnd - loadEventStart</code></li>
<li>整个页面加载时间：<code>loadEventEnd -navigationStart</code></li>
<li>白屏时间：<code>responseStart-navigationStart</code></li>
</ul>
<h3 id="2-2-performance-getEntries"><a href="#2-2-performance-getEntries" class="headerlink" title="2.2 performance.getEntries()"></a>2.2 performance.getEntries()</h3><blockquote>
<p>这个API能帮我们获得资源的请求时间，包括JS、CSS、图片等</p>
</blockquote>
<p><img src="https://img-blog.csdn.net/20170316155201356" alt></p>
<blockquote>
<p>如上图可以看到这个API请求返回的是一个数组，这个数组包括整个页面所有的资源加载，上图打开了一个其中一个资源，可以看到如下信息</p>
</blockquote>
<ul>
<li><code>entryType</code>：类型为<code>resource</code></li>
<li><code>name</code>：资源的<code>url</code></li>
<li><code>initiatorType</code>：资源是<code>link</code></li>
<li>资源时间：<code>duration</code>的值，是<code>responseEnd - startTime</code>得到的</li>
</ul>
<h3 id="2-3-performance-memory"><a href="#2-3-performance-memory" class="headerlink" title="2.3 performance.memory"></a>2.3 performance.memory</h3><blockquote>
<p>这个API主要是得到浏览器内存情况</p>
</blockquote>
<ul>
<li><code>jsHeapSizeLimit</code>：内存大小限制</li>
<li><code>totalJSHeapSize</code>：可使用的内容</li>
<li><code>userdJSHeapSize</code>：已使用的内容</li>
</ul>
<blockquote>
<p><code>userdJSHeapSize</code>表示所有被使用的JS堆栈内存，<code>totalJSHeapSize</code>可使用的JS堆栈内存，如果<code>userdJSHeapSize</code>的值大于<code>totalJSHeapSize</code>，就可能出现内存泄漏</p>
</blockquote>
<p><img src="https://img-blog.csdn.net/20170316155226893" alt></p>
<h2 id="三、动画帧率FPS"><a href="#三、动画帧率FPS" class="headerlink" title="三、动画帧率FPS"></a>三、动画帧率FPS</h2><h3 id="3-1-流畅动画的标准"><a href="#3-1-流畅动画的标准" class="headerlink" title="3.1 流畅动画的标准"></a>3.1 流畅动画的标准</h3><blockquote>
<p><code>FPS</code> 表示的是每秒钟画面更新次数。我们平时所看到的连续画面都是由一幅幅静止画面组成的，每幅画面称为一帧，<code>FPS</code> 是描述“帧”变化速度的物理量</p>
</blockquote>
<ul>
<li>理论上说，<code>FPS</code> 越高，动画会越流畅，目前大多数设备的屏幕刷新率为 <code>60</code> 次/秒，所以通常来讲 <code>FPS</code> 为 <code>60 frame/s</code> 时动画效果最好，也就是每帧的消耗时间为 <code>16.6</code>7ms</li>
</ul>
<p><strong>不同帧率的体验</strong></p>
<ul>
<li>帧率能够达到 <code>50 ～ 60 FPS</code> 的动画将会相当流畅，让人倍感舒适；</li>
<li>帧率在 <code>30 ～ 50 FPS</code> 之间的动画，因各人敏感程度不同，舒适度因人而异；</li>
<li>帧率在 <code>30 FPS</code> 以下的动画，让人感觉到明显的卡顿和不适感；</li>
<li>帧率波动很大的动画，亦会使人感觉到卡顿</li>
</ul>
<h3 id="3-2-获取我们页面动画当前的-FPS-值"><a href="#3-2-获取我们页面动画当前的-FPS-值" class="headerlink" title="3.2 获取我们页面动画当前的 FPS 值"></a>3.2 获取我们页面动画当前的 FPS 值</h3><blockquote>
<p>那么我们该如何准确的获取我们页面动画当前的 FPS 值呢？</p>
</blockquote>
<h4 id="3-2-1-方法一-借助-Chrome-开发者工具"><a href="#3-2-1-方法一-借助-Chrome-开发者工具" class="headerlink" title="3.2.1 方法一 借助 Chrome 开发者工具"></a>3.2.1 方法一 借助 Chrome 开发者工具</h4><blockquote>
<p><code>Chrome</code> 提供给开发者的功能十分强大，在开发者工具中，我们进行如下选择调出 <code>FPS meter</code> 选项：</p>
</blockquote>
<p><img src="https://user-images.githubusercontent.com/8554143/33823997-e25d42dc-de97-11e7-92e3-8521ede019df.png" alt></p>
<blockquote>
<p>通过这个按钮，可以开启页面实时 <code>Frame Rate</code> (帧率) 观测及页面 <code>GPU</code> 使用率</p>
</blockquote>
<p><strong>但是这个方法缺点太多了</strong></p>
<ul>
<li>这个只能一次观测一到几个页面，而且需要人工实时观测</li>
<li>数据只能是主观感受，并没有一个十分精确的数据不断上报或者被收集</li>
<li>因此，我们需要更加智能的方法。</li>
</ul>
<h4 id="3-2-2-方法二-借助-Frame-Timing-API"><a href="#3-2-2-方法二-借助-Frame-Timing-API" class="headerlink" title="3.2.2 方法二 借助 Frame Timing API"></a>3.2.2 方法二 借助 Frame Timing API</h4><blockquote>
<p>Blink 内核早期架构</p>
</blockquote>
<ul>
<li>以 <code>Chrome</code> 浏览器内核 <code>Blink</code> 渲染页面为例。对早期的 <code>Chrome</code> 浏览器而言，每个页面 <code>Tab</code> 对应一个独立的 <code>renderer</code> 进程，<code>Renderer</code>进程中包含了主线程和合成线程。早期 <code>Chrome</code> 内核架构</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/8554143/33830985-4ab23108-deb1-11e7-89ed-6b9f60dcb2fd.jpg" alt></p>
<p><strong>其中，主线程主要负责</strong>：</p>
<ul>
<li><code>Javascript</code> 的计算与执行</li>
<li><code>CSS</code> 样式计算</li>
<li><code>Layout</code> 计算</li>
<li>将页面元素绘制成位图（<code>paint</code>），也就是光栅化（<code>Raster</code>）</li>
<li>将位图给合成线程</li>
</ul>
<p><strong>合成线程则主要负责</strong>：</p>
<ul>
<li>将位图(<code>GraphicsLayer</code> 层)以纹理(<code>texture</code>)的形式上传给 <code>GPU</code></li>
<li>计算页面的可见部分和即将可见部分（滚动）</li>
<li><code>CSS</code> 动画处理</li>
<li>通知 GPU 绘制位图到屏幕上</li>
</ul>
<blockquote>
<p>其实知道了这两个线程之后，下一个概念是厘清 <code>CSS</code> 动画与 <code>JS</code> 动画的细微区别（当然它们都是 Web 动画）</p>
</blockquote>
<p><strong>JS 动画与 CSS 动画的细微区别</strong></p>
<ul>
<li>对于 <code>JS</code>动画而言，它们运行时的帧率即是主线程和合成线程加起来消耗的时间。对于流畅动画而言，我们希望它们每一帧的耗时保持在 <code>16.67ms</code> 之内;</li>
<li>而对于 <code>CSS</code> 动画而言，由于其流程不受主线程的影响，所以希望能得到合成线程的消耗的时间，而合成线程的绘制频率也反映了滚动和 CSS 动画的流程性。</li>
</ul>
<blockquote>
<p>上面主要想得出的一个结论是。如果我们能够知道主线程和合成线程每一帧消耗的时间，那么我们就能大致得出对应的 Web 动画的帧率。那么上面说到的 <code>Frame Timing API</code> 是否可以帮助我们拿到这个时间点呢</p>
</blockquote>
<p><strong>什么是 Frame Timing API ？</strong></p>
<blockquote>
<p><code>Frame Timing API</code> 是 <code>Web Performance Timing API</code> 标准中的其中一位成员。<code>Web Performance Timing API</code> 是 W3C 推出的一套性能 API 标准，用于帮助开发者对网站各方面的性能进行精确的分析与控制，提升 Web 网站性能</p>
</blockquote>
<p>它包含许多子类 API，完成不同的功能，大致如下</p>
<p><img src="https://user-images.githubusercontent.com/8554143/33871575-0799a66a-df4e-11e7-85fd-9b40d43161da.png" alt></p>
<p>怎么使用呢？以 <code>Navigation Timing</code>, <code>Performance Timeline</code>, <code>Resource Timing</code> 为例子，对于兼容它的浏览器，它以只读属性的形式对外暴露挂载在 <code>window.performance</code> 上。</p>
<p>我们再来回顾这张图</p>
<p><img src="https://user-images.githubusercontent.com/8554143/33872196-d6c35844-df50-11e7-8bcc-1fdcac66ce64.png" alt></p>
<blockquote>
<p>通过这张图以及上面的 <code>window.performance.timing</code>，我们就可以轻松的统计出页面每个重要节点的耗时，这就是 <code>Web Performance Timing API</code> 的强大之处，感兴趣的可以详细去研究研究，使用在页面统计上</p>
</blockquote>
<p><strong>Frame Timing API 示意</strong></p>
<blockquote>
<p>终于可以回归正题，借助 <code>Web Performance Timing API</code> 中的<code>Frame Timing API</code>，可以轻松的拿到每一帧中，主线程以及合成线程的时间。或者更加容易，直接拿到每一帧的耗时</p>
</blockquote>
<p>获取 <code>Render</code> 主线程和合成线程的记录，每条记录包含的信息基本如下</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">var rendererEvents = window.performance.getEntriesByType(&quot;renderer&quot;);</span><br><span class="line">var compositeThreadEvents = window.performance.getEntriesByType(&quot;composite&quot;);</span><br></pre></td></tr></table></figure>
<p>或者是：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ar observer = <span class="keyword">new</span> PerformanceObserver(<span class="function"><span class="keyword">function</span>(<span class="params">list</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> perfEntries = list.getEntries();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; perfEntries.length; i++) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"frame: "</span>, perfEntries[i]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"> </span><br><span class="line"><span class="comment">// subscribe to Frame Timing</span></span><br><span class="line">observer.observe(&#123;<span class="attr">entryTypes</span>: [<span class="string">'frame'</span>]&#125;);</span><br></pre></td></tr></table></figure>
<p>每条记录包含的信息基本如下：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  sourceFrameNumber: <span class="number">120</span>,</span><br><span class="line">  startTime: <span class="number">1342.549374253</span></span><br><span class="line">  cpuTime: <span class="number">6.454313323</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>每个记录都包括唯一的 <code>Frame Number</code>、<code>Frame</code> 开始时间以及 <code>cpuTime</code> 时间。通过计算每一条记录的 <code>startTime</code> ，我们就可以算出每两帧间的间隔，从而得到动画的帧率是否能够达到 <code>60 FPS</code></p>
</blockquote>
<p><strong>看看 Web Performance Timing API 整体的兼容性</strong></p>
<p><img src="https://user-images.githubusercontent.com/8554143/33873788-4ad802e2-df57-11e7-9174-13913bcaa9b5.png" alt></p>
<blockquote>
<p><code>Frame Timing API</code> 虽好，但是，现在 <code>Frame Timing API</code> 的兼容性不算很友好，额，不友好到什么程度呢。还没有任何浏览器支持，处于实验性阶段，属于面向未来编程</p>
</blockquote>
<h4 id="3-2-3-方法三-借助-requestAnimationFrame-API"><a href="#3-2-3-方法三-借助-requestAnimationFrame-API" class="headerlink" title="3.2.3 方法三 借助 requestAnimationFrame API"></a>3.2.3 方法三 借助 requestAnimationFrame API</h4><blockquote>
<p>从上面的介绍，我们得知，如果我们可以到得到每一帧中的固定一个时间点，那么两者相减，也能够近似得到一帧所消耗的时间</p>
</blockquote>
<p>这次，我们借助兼容性不错的 <code>requestAnimationFrame API</code></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// 语法</span><br><span class="line">window.requestAnimationFrame(callback);</span><br></pre></td></tr></table></figure>
<ul>
<li><code>requestAnimationFrame</code> 大家应该都不陌生，方法告诉浏览器您希望执行动画并请求浏览器调用指定的函数在下一次重绘之前更新动画。</li>
<li>当你准备好更新屏幕画面时你就应用此方法。这会要求你的动画函数在浏览器下次重绘前执行。回调的次数常是每秒 <code>60</code> 次，大多数浏览器通常匹配 <code>W3C</code> 所建议的刷新率</li>
</ul>
<p><strong>使用 requestAnimationFrame 计算 FPS 原理</strong></p>
<blockquote>
<p>原理是，正常而言 <code>requestAnimationFrame</code> 这个方法在一秒内会执行 <code>60</code> 次，也就是不掉帧的情况下。假设动画在时间 <code>A</code> 开始执行，在时间 <code>B</code> 结束，耗时 <code>x ms</code>。而中间 <code>requestAnimationFrame</code> 一共执行了 <code>n</code> 次，则此段动画的帧率大致为：<code>n / (B - A)</code></p>
</blockquote>
<p>核心代码如下，能近似计算每秒页面帧率，以及我们额外记录一个 <code>allFrameCount</code>，用于记录 <code>rAF</code> 的执行次数，用于计算每次动画的帧率 ：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> rAF = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        <span class="built_in">window</span>.requestAnimationFrame ||</span><br><span class="line">        <span class="built_in">window</span>.webkitRequestAnimationFrame ||</span><br><span class="line">        <span class="function"><span class="keyword">function</span> (<span class="params">callback</span>) </span>&#123;</span><br><span class="line">            <span class="built_in">window</span>.setTimeout(callback, <span class="number">1000</span> / <span class="number">60</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    );</span><br><span class="line">&#125;();</span><br><span class="line">  </span><br><span class="line"><span class="keyword">var</span> frame = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">var</span> allFrameCount = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">var</span> lastTime = <span class="built_in">Date</span>.now();</span><br><span class="line"><span class="keyword">var</span> lastFameTime = <span class="built_in">Date</span>.now();</span><br><span class="line">  </span><br><span class="line"><span class="keyword">var</span> loop = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> now = <span class="built_in">Date</span>.now();</span><br><span class="line">    <span class="keyword">var</span> fs = (now - lastFameTime);</span><br><span class="line">    <span class="keyword">var</span> fps = <span class="built_in">Math</span>.round(<span class="number">1000</span> / fs);</span><br><span class="line">  </span><br><span class="line">    lastFameTime = now;</span><br><span class="line">    <span class="comment">// 不置 0，在动画的开头及结尾记录此值的差值算出 FPS</span></span><br><span class="line">    allFrameCount++;</span><br><span class="line">    frame++;</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">if</span> (now &gt; <span class="number">1000</span> + lastTime) &#123;</span><br><span class="line">        <span class="keyword">var</span> fps = <span class="built_in">Math</span>.round((frame * <span class="number">1000</span>) / (now - lastTime));</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;<span class="keyword">new</span> <span class="built_in">Date</span>()&#125;</span> 1S内 FPS：`</span>, fps);</span><br><span class="line">        frame = <span class="number">0</span>;</span><br><span class="line">        lastTime = now;</span><br><span class="line">    &#125;;</span><br><span class="line">  </span><br><span class="line">    rAF(loop);</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">loop();</span><br></pre></td></tr></table></figure>
<p>寻找一个有动画不断运行的页面进行测试，可以看到代码运行如下：</p>
<p><img src="https://user-images.githubusercontent.com/8554143/33877304-eae6fddc-df62-11e7-8d5f-8674dba3d1f9.png" alt></p>
<ul>
<li>使用了我之前制作的一个页面进行了测试，使用<code>Chrome</code> 同时调出页面的 <code>FPS meter</code>，对比两边的实时 <code>FPS</code> 值，基本吻合。</li>
<li>测试页面，<code>Solar System</code>。你可以将上面的代码贴到这个页面的 <code>console</code> 中，测试一下数据</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/8554143/33877626-ed17f3c6-df63-11e7-8bae-40b9512c2645.gif" alt></p>
<ul>
<li>对比右上角的 <code>Frame Rate</code>，帧率基本一致。在大部分情况下，这种方法可以很好的得出 Web 动画的帧率。</li>
<li>如果我们需要统计某个特定动画过程的帧率，只需要在动画开始和结尾两处分别记录 <code>allFrameCount</code> 这个数值大小，再除以中间消耗的时间，也可以得出特定动画过程的 <code>FPS</code> 值。</li>
<li>值得注意的是，这个方法计算的结果和真实的帧率肯定是存在误差的，因为它是将每两次主线程执行 <code>javascript</code> 的时间间隔当成一帧，而非上面说的主线程加合成线程所消耗的时间为一帧。但是对于现阶段而言，算是一种可取的方法</li>
</ul>

      </div>
    
  </div>

</article>

<button class="assist-btn2 circle" id="assist_btn2" title="点亮屏幕" style="left: 27px; top: 152px;">
  <i class="iconfont" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>
<button class="assist-btn1 circle" id="assist_btn1" title="关闭屏幕亮度" style="left: 27px; top: 152px;">
  <i class="iconfont toc-title" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>


<script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>	

<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
<script>
  const btw = new BTWPlugin();
  btw.init({
    id: "container",
    blogId: "22699-1592137983091-414",
    name: "前端进阶之旅",
    qrcode: "https://poetries1.gitee.io/img-repo/2020/06/qrcode.jpg",
    keyword: "3a3b3c",
  });
</script>

<script type="text/javascript">

// white theme
var body = {color: "#555", background: "#000"};
var a_tag = {color: "#222"};
var header = { background: "#222"};
var logo_line_i = {background: "#222"};
// var post_code = {background: "#eee", color: "#222"};

function switch_theme() {
 $("body").css(body);
 $("a:not('.links-of-author-item a, .site-state-item a, .site-state-posts a, .feed-link a, .motion-element a, .post-tags a, .show-commit-cls a, #donate_board a')").css(a_tag);
 $(".header, .footer").css(header);
 $(".logo-line-before i, .logo-line-after i").css(logo_line_i);
 //$(".post code").css(post_code);
 $("#idhyt-surprise-ball #idhyt-surprise-ball-animation .drag").css(a_tag);
 $(".post-title-link, .posts-expand .post-meta, .post-comments-count, .disqus-comment-count, .post-category a, .post-nav-next a, .post-nav-item a").css(a_tag);
 
 // $("code").css({color: '#c5c8c6', background: '#1d1f21'});
 //$("#assist_btn1").hide(1500);
}

$(function () {
$("#assist_btn2").css("display","none");
 $("#assist_btn1").click(function() {
     switch_theme();
$("div#toc.toc-article").css({
 "background":"#eaeaea",
 "opacity":1
});
$(".toc-article ol").show();
$("#toc.toc-article .toc-title").css("color","#a98602");
$("#assist_btn1").css("display","none");
$("#assist_btn2").css("display","block");
 });
$("#assist_btn2").click(function() {
$("#assist_btn2").css("display","none");
$("#assist_btn1").css("display","block");
$("body").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
     $(".header, .footer").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
$(".toc-article ol").toggle(1000);
 });
});


//背景随机

var Y, O, E, L, B, C, T, z, N, S, A, I;
!function() {
var e = function() {
for (O.clearRect(0, 0, L, B), T = [{
x: 0,
y: .7 * B + C
}, {
x: 0,
y: .7 * B - C
}]; T[1].x < L + C;) t(T[0], T[1])
}, t = function(e, t) {
O.beginPath(), O.moveTo(e.x, e.y), O.lineTo(t.x, t.y);
var n = t.x + (2 * I() - .25) * C,
 r = a(t.y);
O.lineTo(n, r), O.closePath(), N -= S / -50, O.fillStyle = "#" + (127 * A(N) + 128 << 16 | 127 * A(N + S / 3) + 128 << 8 | 127 * A(N + S / 3 * 2) + 128).toString(16), O.fill(), T[0] = T[1], T[1] = {
 x: n,
 y: r
}
}, a = function n(e) {
var t = e + (2 * I() - 1.1) * C;
return t > B || t < 0 ? n(e) : t
};
Y = document.getElementById("evanyou"), O = Y.getContext("2d"), E = window.devicePixelRatio || 1, L = window.innerWidth, B = window.innerHeight, C = 90, z = Math, N = 0, S = 2 * z.PI, A = z.cos, I = z.random, Y.width = L * E, Y.height = B * E, O.scale(E, E), O.globalAlpha = .6, document.onclick = e, document.ontouchstart = e, e()
}()

   
$("#toc-eye").click(function(){
$("#toc.toc-article").toggle(1000);
});

</script>


   
  <div class="text-center donation">
    <div class="inner-donation">
      <span class="btn-donation">支持一下</span>
      <div class="donation-body">
        <div class="tip text-center">扫一扫，支持poetries</div>
        <ul>
        
          <li class="item">
            
              <span>微信扫一扫</span>
            
            <img src="/images/weixin.jpg" alt="">
          </li>
        
          <li class="item">
            
              <span>支付宝扫一扫</span>
            
            <img src="/images/zhifubao.jpg" alt="">
          </li>
        
        </ul>
      </div>
    </div>
  </div>


   
  <div class="box-prev-next clearfix">
    <a class="show pull-left" href="/2018/11/20/hexo-chat/">
        <i class="icon icon-angle-left"></i>
    </a>
    <a class="show pull-right" href="/2018/11/20/webpack-config-optize/">
        <i class="icon icon-angle-right"></i>
    </a>
  </div>




</div>


  <a id="backTop" class="back-top">
    <i class="icon-angle-up"></i>
  </a>




  <div class="modal" id="modal">
  <span id="cover" class="cover hide"></span>
  <div id="modal-dialog" class="modal-dialog hide-dialog">
    <div class="modal-header">
      <span id="close" class="btn-close">关闭</span>
    </div>
    <hr>
    <div class="modal-body">
      <ul class="list-toolbox">
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/archives/"
              rel="noopener noreferrer"
              target="_self"
              >
              博客
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/categories/"
              rel="noopener noreferrer"
              target="_self"
              >
              分类
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/tags/"
              rel="noopener noreferrer"
              target="_self"
              >
              标签
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/search/"
              rel="noopener noreferrer"
              target="_self"
              >
              搜索
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/link/"
              rel="noopener noreferrer"
              target="_self"
              >
              友链
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/about/"
              rel="noopener noreferrer"
              target="_self"
              >
              关于
            </a>
          </li>
        
      </ul>

    </div>
  </div>
</div>



  
      <div class="fexo-comments comments-post">
    

    

    
    

    

    
    

    

<!-- Gitalk评论插件通用代码 -->
<div id="gitalk-container"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script>
const gitalk = new Gitalk({
  clientID: '5567a2c4abb858009d96',
  clientSecret: 'b9039ec056cf5c2346b3cdb63308a28c163f91e5',
  repo: 'poetries.github.io',
  owner: 'poetries',
  // 在这里设置一下截取前50个字符串, 这是因为 github 对 label 的长度有了要求, 如果超过
  // 50个字符串则会报错.
  // id: location.pathname.split('/').pop().substring(0, 49),
  id: location.pathname,
  admin: ['poetries'],
  // facebook-like distraction free mode
  distractionFreeMode: false
})
gitalk.render('gitalk-container')
</script>
<!-- Gitalk代码结束 -->



  </div>

  

  <script type="text/javascript">
  function loadScript(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript';

    if (script.readyState) { //IE
      script.onreadystatechange = function() {
        if (script.readyState == 'loaded' ||
          script.readyState == 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      };
    } else { //Others
      script.onload = function() {
        callback();
      };
    }

    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  window.onload = function() {
    loadScript('/js/bundle.js?235683', function() {
      // load success
    });
  }
</script>


  <!-- 页面点击小红心 -->
  <script type="text/javascript" src="/js/clicklove.js"></script>
 
  
</body>
</html>
